package com.example.demo.service.exam.impl;


import com.example.demo.base.config.auth.UserInfoShareHolder;
import com.example.demo.base.login.dto.UserCache;
import com.example.demo.domain.CombineClause;
import com.example.demo.domain.PageResult;
import com.example.demo.domain.SingleClause;
import com.example.demo.domain.entity.User;
import com.example.demo.domain.exam.Exam;
import com.example.demo.domain.exam.ExamClass;
import com.example.demo.domain.exam.ExamClassRelation;
import com.example.demo.domain.exam.ExamUserScore;
import com.example.demo.domain.exam.Paging;
import com.example.demo.domain.exam.QueryModel;
import com.example.demo.domain.exam.condition.ConditionParser;
import com.example.demo.domain.exam.condition.ExamClassCondition;
import com.example.demo.domain.exam.condition.ExamClassStaffCondition;
import com.example.demo.domain.exam.condition.ExamStaffCondition;
import com.example.demo.dto.ExamClassRelationDTO;
import com.example.demo.dto.ExamClassStaffDTO;
import com.example.demo.dto.TrainingClassDTO;
import com.example.demo.enums.ExamScoreStatus;
import com.example.demo.exception.ExceptionUtils;
import com.example.demo.mapper.ExamClassMapper;
import com.example.demo.service.exam.ExamService;
import com.example.demo.service.exam.ExamUserScoreService;
import com.example.demo.service.impl.BaseServiceImpl;
import com.example.demo.service.exam.ExamClassRelationService;
import com.example.demo.service.exam.ExamClassService;
import lombok.RequiredArgsConstructor;
import org.apache.commons.lang3.BooleanUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
public class ExamClassServiceImpl extends BaseServiceImpl<ExamClassMapper, ExamClass> implements ExamClassService {

    private static final Long INTER_OTHER_EXAM_CLASS_ID = 0L;

    private static final String STRING_OTHER_EXAM_CLASS_NAME = "其他用户";

    private final ExamClassMapper mapper;

    private final ExamClassRelationService examClassRelationService;

    private final ApplicationContext applicationContext;

    @Override
    public void createExamClass(ExamClass entity) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        Long companyId = userInfo.getCompanyId();
        User user = userInfo.getUser();
        Long userId = user.getId();
        entity.setCreatorId(userId);
        entity.setCompanyId(companyId);
        this.valid(entity);
        entity.setCreateTime(new Timestamp(System.currentTimeMillis()));
        entity.setDisabled(false);
        mapper.insert(entity);
    }

    @Override
    public void updateExamClass(ExamClass entity) {
        if (entity.getId() == null) {
            throw ExceptionUtils.exception("所更新考试班级标识不能为空");
        }
        this.valid(entity);
        entity.setModifyTime(new Timestamp(System.currentTimeMillis()));
        mapper.updateById(entity);
    }

    @Override
    public PageResult<ExamClass> page(QueryModel<ExamClassCondition> model) {
        UserCache userInfo = UserInfoShareHolder.getUserInfo();
        ExamClassCondition condition = Optional.ofNullable(model.getCondition()).orElse(new ExamClassCondition());
        CombineClause clauses = CombineClause.and(
                ConditionParser.parser(condition),
                SingleClause.equal("disabled", false),
                SingleClause.equal("creator_id", userInfo.getUser().getId())
        );
        return super.selectPage(clauses, model.getPaging(), model.getSorts());
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addStaff(ExamClassRelationDTO dto) {
        Long examClassId = Optional.ofNullable(dto.getClassId()).orElseThrow(() -> ExceptionUtils.exception("考试班级标识不能为空"));
        Optional.ofNullable(mapper.selectById(examClassId)).orElseThrow(() -> ExceptionUtils.exception("考试班级不存在"));
        examClassRelationService.deleteExamClassRelationsByClassId(examClassId);
        examClassRelationService.createExamClassRelations(dto);
    }

    @Override
    @Transactional(rollbackFor = Exception.class)
    public void addStaffForExamClass(ExamClassRelationDTO dto) {
        checkExamClass(dto);
        Long classId = dto.getClassId();
        List<Long> userIds = dto.getUserIds();
        if (CollectionUtils.isEmpty(userIds)) {
            return;
        }
        Map<Long, Boolean> map = examClassRelationService.getExamClassRelationsByClassId(classId).stream().collect(Collectors.toMap(ExamClassRelation::getUserId, e -> true));
        userIds.forEach(e -> {
            if (BooleanUtils.isTrue(map.get(e))) {
                throw ExceptionUtils.exception("该用户已在班级中");
            }
        });
        examClassRelationService.createExamClassRelations(dto);
        ExamService bean = applicationContext.getBean(ExamService.class);
        Map<Long, String> userMap = getUserByUserIds(userIds).stream().collect(Collectors.toMap(User::getId, User::getUsername));
        List<Exam> examList = bean.getExamByClassId(classId);
        List<ExamUserScore> scores = new ArrayList<>();
        examList.forEach(e -> {
            for (Long userId : userIds) {
                ExamUserScore examUserScore = new ExamUserScore();
                examUserScore.setStatus(ExamScoreStatus.NOT_STARTED);
                examUserScore.setExamId(e.getId());
                examUserScore.setClassId(classId);
                examUserScore.setUserId(userId);
                examUserScore.setExamName(e.getExamName());
                examUserScore.setUsername(userMap.get(userId));
                scores.add(examUserScore);
            }
        });
        ExamUserScoreService scoreService = applicationContext.getBean(ExamUserScoreService.class);
        scoreService.insertList(scores);
    }

    @Override
    public List<User> getUserByUserIds(List<Long> userIds) {
        if (CollectionUtils.isEmpty(userIds)) {
            return Collections.emptyList();
        }
        return mapper.getUserByUserIds(userIds);
    }

    @Override
    public List<ExamClass> getByCreatorId(Long creatorId, Long companyId) {
        if (creatorId == null || companyId == null) {
            return Collections.emptyList();
        }
        return super.selectList(CombineClause.and(
                SingleClause.equal("creator_id", creatorId),
                SingleClause.equal("company_id", companyId)
        ));
    }

    @Override
    public void deleteStaffForExamClass(ExamClassRelationDTO dto) {
        checkExamClass(dto);
        examClassRelationService.deleteByClassUserId(dto);
    }

    private void checkExamClass(ExamClassRelationDTO dto) {
        Long examClassId = Optional.ofNullable(dto.getClassId()).orElseThrow(() -> ExceptionUtils.exception("考试班级标识不能为空"));
        Optional.ofNullable(mapper.selectById(examClassId)).orElseThrow(() -> ExceptionUtils.exception("考试班级不存在"));
    }

    @Override
    public List<TrainingClassDTO> getTrainingClass() {
//        UserCache userInfo = UserInfoShareHolder.getUserInfo();
//        Long companyId = userInfo.getCompanyId();
//        User user = userInfo.getUser();
//        Long userId = user.getId();
        List<TrainingClassDTO> list = Optional.ofNullable(mapper.getTrainingClass(0L)).orElse(new ArrayList<>());
        TrainingClassDTO dto = new TrainingClassDTO();
        dto.setId(INTER_OTHER_EXAM_CLASS_ID);
        dto.setName(STRING_OTHER_EXAM_CLASS_NAME);
        list.add(dto);
        return list;
    }

    @Override
    public List<ExamClassStaffDTO> getStaff(Long classId) {
        Optional.ofNullable(classId).orElseThrow(() -> ExceptionUtils.exception("班级标识不能为空"));
        return classId.equals(INTER_OTHER_EXAM_CLASS_ID) ?
                mapper.getUserByNotInTrainingClass() : mapper.getUserByClassId(classId);
    }

    @Override
    public ExamClass getByClassId(Long classId) {
        Optional.ofNullable(classId).orElseThrow(() -> ExceptionUtils.exception("班级标识不能为空"));
        return mapper.selectById(classId);
    }

    @Override
    public PageResult<ExamClassStaffDTO> pageExamClassStaff(QueryModel<ExamClassStaffCondition> model) {
        ExamClassStaffCondition condition = Optional.ofNullable(model.getCondition()).orElse(new ExamClassStaffCondition());
        if (condition.getClassId() == null) {
            throw ExceptionUtils.exception("班级标识不能为空");
        }
        Paging paging = Optional.ofNullable(model.getPaging()).orElse(new Paging());
        paging.setPageIndex((paging.getPageIndex() - 1) * paging.getPageSize());
        List<ExamClassStaffDTO> dto = mapper.pageExamClassStaff(condition, paging);
        Long count = mapper.countExamClassStaff(condition, paging);
        return PageResult.of(dto).totalCount(count);
    }

    @Override
    public Map<Long, String> getNameMapByIds(List<Long> classIds) {
        if (CollectionUtils.isEmpty(classIds)) {
            return Collections.emptyMap();
        }
        return super.selectList(SingleClause.in("id", classIds.toArray(new Object[0]))).stream()
                .collect(Collectors.toMap(ExamClass::getId, ExamClass::getClassName));
    }

    @Override
    public List<ExamClassStaffDTO> pageStaff(QueryModel<ExamStaffCondition> model) {
        ExamStaffCondition condition = Optional.ofNullable(model.getCondition()).orElse(new ExamStaffCondition());
        Long id = condition.getId();
        Optional.ofNullable(id).orElseThrow(() -> ExceptionUtils.exception("班级标识不能为空"));
        Optional.ofNullable(condition.getClassId()).orElseThrow(() -> ExceptionUtils.exception("考试标识不能为空"));
        List<ExamClassStaffDTO> dto = id.equals(INTER_OTHER_EXAM_CLASS_ID) ? mapper.pageUserByNotInTrainingClass(condition, model.getPaging()) : mapper.pageUserById(condition, model.getPaging());
        Map<Long, Boolean> map = examClassRelationService.getExamClassRelationsByClassId(condition.getClassId()).stream().collect(Collectors.toMap(ExamClassRelation::getUserId, e -> true));
        dto.forEach(e -> e.setCheck(map.getOrDefault(e.getUserId(), false)));
        return dto;
    }

    private void valid(ExamClass entity) {
        String className = entity.getClassName();
        Long uId = UserInfoShareHolder.getUserInfo().getUser().getId();
        Long companyId = UserInfoShareHolder.getUserInfo().getCompanyId();
        if (StringUtils.isBlank(className)) {
            throw ExceptionUtils.exception("班级名称不能为空");
        }
        boolean exists = super.exists(CombineClause.and(
                SingleClause.equal("class_name", className),
                SingleClause.equal("creator_id", uId),
                SingleClause.equal("company_id", companyId)
//                SingleClause.notEqual("id", entity.getId())
        ));
        if (exists) {
            throw ExceptionUtils.exception("班级名称不能重复：%s", className);
        }
    }

}
